package com.ruoyi.controller.log;



import com.alibaba.fastjson2.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.ExtendedServletRequestDataBinder;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.*;


/**
 * @author 100011568
 */
@Aspect
@Slf4j
@Component
@Order(3)
public class LoggerAspect {

    @Pointcut("execution(* com.ruoyi.controller..*.*(..))")
    public void logging() {
    }

    @Around(value = "logging()")
    public Object loggerAround(ProceedingJoinPoint pjp) throws Throwable {
        HttpServletRequest request = null;
        Enumeration<String> headerNames = null;
        try {
            request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
            headerNames = request.getHeaderNames();
        } catch (Exception e) {
            return pjp.proceed();
        }
        StringBuilder headers = new StringBuilder(64);
        if (headerNames != null) {
            HeaderEnum[] constants = HeaderEnum.values();
            List<String> list = new ArrayList<>();
            while (headerNames.hasMoreElements()) {
                list.add(headerNames.nextElement());
            }
            for (HeaderEnum constant : constants) {
                String name = constant.getHeaderName();
                String headerName = name.toLowerCase();
                for (String header : list) {
                    if (Objects.equals(headerName, header)) {
                        headers.append(name).append(":").append(request.getHeader(headerName)).append(" ");
                        break;
                    }
                }
            }
        }
        String authorization = request.getHeader("Authorization");
        Class<?> targetClass = pjp.getTarget().getClass();

        String targetClassName = targetClass.getSimpleName();
        MethodSignature methodSignature = (MethodSignature) pjp.getSignature();

        long start = System.currentTimeMillis();

        String methodName = methodSignature.getName();
        String ipAddr = getRemoteHost(request);
        String url = request.getRequestURI();

        String prefix = "请求源IP: [" + ipAddr + "], 请求URL: [" + StringUtils.upperCase(request.getMethod()) + " " + url + "], 请求头信息:[ " + headers + "],";

        StringBuilder sb = new StringBuilder(prefix);
        sb.append(targetClassName).append("#").append(methodName).append("运行开始 start ===> 参数列表：");
        Object[] args = pjp.getArgs();
        // 参数名
        String[] argNames = methodSignature.getParameterNames();
        Map<String, Object> paramMap = getParameterMapFromRequest(args, argNames);

        String paramJson;
        try {
            if (!paramMap.isEmpty()) {
                paramJson = JSON.toJSONString(paramMap);
                sb.append(paramJson);
            } else {
                sb.append("无参");
            }

        } catch (Exception e) {
            log.error("序列化参数有误，不影响后续调用： ", e);
            sb.append("序列化参数有误");
        }

        LogPrint classPrint = targetClass.getAnnotation(LogPrint.class);
        if (classPrint != null && classPrint.isPrintRequest()) {
            log.info(sb.toString());
            Object o = null;
            try {
                o = pjp.proceed();
            } finally {
                long end = System.currentTimeMillis();
                String msg = targetClassName + "#" + methodName + "运行结束 end ===> 耗时：[" + (end - start) + "]ms";
                if (classPrint.isPrintResponse()) {
                    log.info(msg + ", the response data : {}", JSON.toJSONString(o));
                } else {
                    log.info(msg);
                }

            }
            return o;
        }

        LogPrint methodPrint = methodSignature.getMethod().getAnnotation(LogPrint.class);
        if (methodPrint != null && methodPrint.isPrintRequest()) {
            log.info(sb.toString());
            Object o = null;
            try {
                o = pjp.proceed();
            } finally {
                long end = System.currentTimeMillis();
                String msg = targetClassName + "#" + methodName + "运行结束 end ===> 耗时：[" + (end - start) + "]ms";
                if (methodPrint.isPrintResponse()) {
                    log.info(msg + ", the response data : {}", JSON.toJSONString(o));
                } else {
                    log.info(msg);
                }

            }
            return o;
        }

        Object o;
        try {
            log.info(sb.toString());
            o = pjp.proceed();
            return o;
        } finally {
            long end = System.currentTimeMillis();
            String msg = targetClassName + "#" + methodName + "运行结束 end ===> 耗时：[" + (end - start) + "]ms";
            log.info(msg);
        }
    }

    private Map<String, Object> getParameterMapFromRequest(Object[] args, String[] argNames) {
        Map<String, Object> paramMap = new HashMap<>();
        for (int i = 0; i < args.length; i++) {
            if (!(args[i] instanceof ExtendedServletRequestDataBinder
                    || args[i] instanceof ServletRequest
                    || args[i] instanceof ServletResponse
                    || args[i] instanceof HttpSession
                    || args[i] instanceof MultipartFile
                    || args[i] instanceof BindingResult
//                    || args[i] instanceof Page
            )) {
                if (ObjectUtils.isNotEmpty(argNames) && ObjectUtils.isNotEmpty(argNames[i])){
                    paramMap.put(argNames[i], args[i]);
                }else {
                    paramMap.put(argNames[i], "");
                }
            }
//            else if (args[i] instanceof Page page) {
//                paramMap.put("pageIndex", page.getPageIndex());
//                paramMap.put("pageSize", page.getPageSize());
//            }
        }
        return paramMap;
    }

    private String getRemoteHost(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }


        return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip;
    }

}